package com.leenmvc.core.security;

import com.leenmvc.core.cache.CacheFactory;
import com.leenmvc.core.enums.CacheType;
import com.leenmvc.core.exception.AuthorizedException;
import com.leenmvc.core.init.Constant;
import com.leenmvc.core.security.dao.core.SysUserDao;
import com.leenmvc.core.security.dao.core.SysUserTokenDao;
import com.leenmvc.core.security.entity.core.SysUser;
import com.leenmvc.core.security.entity.core.SysUserToken;
import com.leenmvc.core.utils.thread.ThreadLocalManager;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import java.util.Set;

/**
 * 认证领域
 *
 * @author leen
 * @version 2.5.1
 * @since 2018-01-10
 */
@Configuration
public class AuthRealm extends AuthorizingRealm {

    private final static Logger logger = LoggerFactory.getLogger(AuthorizingRealm.class);

    @Autowired
    private SysUserDao sysUserDao;

    @Autowired
    private SysUserTokenDao sysUserTokenDao;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof Oauth2Token;
    }

    /**
     * 重写鉴权方法
     *
     * @param principals
     * @param permission
     * @return
     */
    @Override
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        if(Constant.dev_mode) return true;
        List<SysUser> userList = principals.asList();
        SysUser sysUser = userList.get(0);
        if(sysUser.getSuperAdmin() != null && sysUser.getSuperAdmin() == 1) {
            return true;
        } else {
            Set<String> permisions = sysUser.getPermisions();
            if(permisions.contains(permission)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
        return true;
    }

	/**
     * 认证回调函数,登录时调用
     * 
     * 用户的角色信息
     * 
     * 首先根据传入的用户名获取User信息；然后如果user为空，那么抛出没找到帐号异常UnknownAccountException；
     * 如果user找到但锁定了抛出锁定异常LockedAccountException；最后生成AuthenticationInfo信息，
     * 交给间接父类AuthenticatingRealm使用CredentialsMatcher进行判断密码是否匹配，
     * 如果不匹配将抛出密码错误异常IncorrectCredentialsException；
     * 另外如果密码重试次数太多将抛出超出重试次数异常ExcessiveAttemptsException；
     * 在组装SimpleAuthenticationInfo信息时， 需要传入：身份信息（用户名）、凭据（密文密码）、盐（username+salt），
     * CredentialsMatcher使用盐加密传入的明文密码和此处的密文密码进行匹配。
     * 
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        String principal = (String) token.getPrincipal();
        SysUser user = CacheFactory.get(CacheType.USER_INFO, principal);
        SysUserToken userToken = null;
        if(user == null) { // 如果用户
            userToken = sysUserTokenDao.get("token", principal);
            if(userToken != null) {
                user = sysUserDao.get(userToken.getUserId());
                user.setPermisions(sysUserDao.getUserPermissions(user.getId()));
                user.setToken(userToken);
            }
        } else {
            userToken = user.getToken();
        }

        if (userToken == null) {
            throw new AuthorizedException("当前授权已过期或者未登录，请重新登录！！");
        }

        if(!user.getToken().getToken().equals(userToken.getToken())){
            CacheFactory.remove(CacheType.USER_INFO, principal);
            throw new AuthorizedException("当前授权已过期或者未登录，请重新登录！！");
        }

        if(user.getToken() == null) {
            throw new AuthorizedException("当前授权已过期或者未登录，请重新登录！！");
        }

        // token过期验证
        if(user.getToken().getExpireDate().getTime() < System.currentTimeMillis()) {
            throw new AuthorizedException("当前授权已经过期！！");
        }

        CacheFactory.put(CacheType.USER_INFO, principal, user);
        ThreadLocalManager.setUser(user);
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, principal, getName());
        return authenticationInfo;
    }

    /**
     *
     * 验证权限的时候调用本方法？
     *
     * 用户拥有哪些角色？
     *
     * 角色可以操作哪些菜单？
     *
     * 菜单需要哪些权限？
     *
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        SysUser user = (SysUser) principal.getPrimaryPrincipal();
        if(user.getToken() == null) {
            throw new AuthorizedException("当前授权已过期或者未登录，请重新登录！！");
        }

        // token过期验证
        if(user.getToken().getExpireDate().getTime() < System.currentTimeMillis()) {
            throw new AuthorizedException("当前授权已经过期！！");
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(user.getPermisions());
        return info;
    }

}